<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <link rel="stylesheet" href="css/ol.css" type="text/css">
    <script src="./js/ol.js" type="text/javascript"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
    <style type="text/css">
        #map {
            width: 100%;
            height: 100%;
            position: absolute;
        }

        #menu {
            float: left;
            position: absolute;
            bottom: 50px;
            right: 30px;
            z-index: 2000;
        }

        .checkbox {
            left: 20px;
        }

        /**
        * 提示框的样式信息
        */
        .tooltip {
            position: relative;
            background: rgba(0, 0, 0, 0.5);
            border-radius: 4px;
            color: white;
            padding: 4px 8px;
            opacity: 0.7;
            white-space: nowrap;
        }

        .tooltip-measure {
            opacity: 1;
            font-weight: bold;
        }

        .tooltip-static {
            background-color: #ffffff;
            color: black;
            border: 1px solid white;
        }

        .tooltip-measure:before,
        .tooltip-static:before {
            border-top: 6px solid rgba(0, 0, 0, 0.5);
            border-right: 6px solid transparent;
            border-left: 6px solid transparent;
            content: "";
            position: absolute;
            bottom: -6px;
            margin-left: -7px;
            left: 50%;
        }

        .tooltip-static:before {
            border-top-color: #ffffff;
        }

        #scalebar {
            float: left;
            margin-bottom: 10px;
        }
    </style>

</head>

<body>
<div id="map">
    <div id="menu">
        <label>测量类型选择</label>
        <select id="type">
            <option value="length">长度</option>
            <option value="area">面积</option>
        </select>
        <label class="checkbox label"><input type="checkbox" id="geodesic" />使用大地测量</label>
    </div>
</div>
<div id="scalebar"></div>
<script type="text/javascript">
    $(function () {
        //格式
        var format = 'image/png';
        var bounds = [73.441277, 18.159829,
            135.08693, 53.561771];//范围

        //中国各省底图（面）
        var ImageMap = new ol.layer.Tile({
            source: new ol.source.TileWMS({
                ratio: 1,
                //自己的服务url
                url: 'http://localhost:8080/geoserver/China_Test/wms',
                //设置服务参数
                params: {
                    'FORMAT': format,
                    'VERSION': '1.1.0',
                    STYLES: '',
                    //图层信息
                    LAYERS: 'China_Test:C_Test',
                }
            })
        });

        //设置地图投影
        var projection = new ol.proj.Projection({
            code: 'EPSG:4326',//投影编码
            units: 'degrees',
            axisOrientation: 'neu'
        });

        //设置地图 实例化一个地图
        var map = new ol.Map({
            //地图中的比例尺等控制要素
            controls: ol.control.defaults({
                attribution: false
            }).extend([
                new ol.control.FullScreen(),//全屏显示
            ]),
            //设置显示的容器
            target: 'map',
            //设置图层
            layers: [
                //添加图层
                ImageMap
            ],
            //设置视图
            view: new ol.View({
                //设置投影
                projection: projection,
                center: [102.73333, 25.05], //初始在某一点
                minZoom: 3,
                zoom: 5, //缩放级别
            }),
        });

        //定义矢量数据源
        var source = new ol.source.Vector();
        //定义矢量图层
        var vector = new ol.layer.Vector({
            source: source,
            style: new ol.style.Style({
                fill: new ol.style.Fill({
                    color: 'rgba(255,255,255,0.2)'
                }),
                stroke: new ol.style.Stroke({
                    color: '#e21e0a',
                    width: 2
                }),
                image: new ol.style.Circle({
                    radius: 5,
                    fill: new ol.style.Fill({
                        color: '#ffcc33'
                    })
                })
            })
        });
        //将矢量图层添加到地图中
        map.addLayer(vector);

        //添加比例尺控件
        var scaleLineControl = new ol.control.ScaleLine({
            units: 'metric',
            target: 'scalebar',
            className: 'ol-scale-line'
        });
        map.addControl(scaleLineControl);


        //实例化鼠标位置控件
        var mousePositionControl = new ol.control.MousePosition({
            coodrdinateFormat: ol.coordinate.createStringXY(4),//坐标格式
            //地图投影坐标系
            projection: new ol.proj.Projection({
                code: 'EPSG:4326',//投影编码
                units: 'degrees',
                axisOrientation: 'neu'
            }),
            //className:'tip',
            target: document.getElementById('tip'),//显示鼠标位置信息的目标容器
            undefinedHTML: '&nbsp;'//未定义坐标标记
        });

        //添加鼠标位置控件
        map.addControl(mousePositionControl);

        //实例化鹰眼控件
        var overviewMapControl = new ol.control.OverviewMap({
            //在鹰眼中相同坐标系下不通数据源的图层
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM({
                        'url': 'http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
                    })
                })
            ],
            collapseLabel: '\u00BB',
            lable: '\u00AB',
            collapsed: false,
        });
        //添加鹰眼
        map.addControl(overviewMapControl);

        //创建一个WGS84球体对象
        var wgs84Sphere = new ol.Sphere(6378137);
        //创建一个当前要绘制的对象
        var sketch = new ol.Feature();
        //创建一个帮助提示框对象
        var helpTooltipElement;
        //创建一个帮助提示信息对象
        var helpTooltip;
        //创建一个测量提示框对象
        var measureTooltipElement;
        //创建一个测量提示信息对象
        var measureTooltip;
        //继续绘制多边形的提示信息
        var continuePolygonMsg = '单击以继续绘制多边形';
        //继续绘制线段的提示信息
        var continueLineMsg = '单击以继续绘制直线';

        //鼠标移动触发的函数
        var pointerMoveHandler = function (evt) {
            //Indicates if the map is currently being dragged.
            //Only set for POINTERDRAG and POINTERMOVE events. Default is false.
            //如果是平移地图则直接结束
            if (evt.dragging) {
                return;
            }
            //帮助提示信息
            var helpMsg = '单击开始';

            if (sketch) {
                //获取绘图对象的几何要素
                var geom = sketch.getGeometry();
                //如果当前绘制的几何要素是多线段，则将绘制提示信息设置为多线段绘制提示信息
                if (geom instanceof ol.geom.Polygon) {
                    helpMsg = continuePolygonMsg;
                } else if (geom instanceof ol.geom.LineString) {
                    helpMsg = continueLineMsg;
                }
            }
            //设置帮助提示要素的内标签为帮助提示信息
            helpTooltipElement.innerHTML = helpMsg;
            //设置帮助提示信息的位置
            helpTooltip.setPosition(evt.coordinate);
            //移除帮助提示要素的隐藏样式
            $(helpTooltipElement).removeClass('hidden');
        };

        //触发pointermove事件
        map.on('pointermove', pointerMoveHandler);

        //当鼠标移除地图视图的时为帮助提示要素添加隐藏样式
        $(map.getViewport()).on('mouseout', function () {
            $(helpTooltipElement).addClass('hidden');
        });

        //获取大地测量复选框
        var geodesicCheckbox = document.getElementById('geodesic');
        //获取类型
        var typeSelect = document.getElementById('type');
        //定义一个交互式绘图对象
        var draw;

        //添加交互式绘图对象的函数
        function addInteraction() {
            // 获取当前选择的绘制类型
            var type = typeSelect.value == 'area' ? 'Polygon' : 'LineString';
            //创建一个交互式绘图对象
            draw = new ol.interaction.Draw({
                //绘制的数据源
                source: source,
                //绘制类型
                type: type,
                //样式
                style: new ol.style.Style({
                    fill: new ol.style.Fill({
                        color: 'rgba(255,255,255,0.2)'
                    }),
                    stroke: new ol.style.Stroke({
                        color: 'rgba(0,0,0,0.5)',
                        lineDash: [10, 10],
                        width: 2
                    }),
                    image: new ol.style.Circle({
                        radius: 5,
                        stroke: new ol.style.Stroke({
                            color: 'rgba(0,0,0,0.7)'
                        }),
                        fill: new ol.style.Fill({
                            color: 'rgba(255,255,255,0.2)'
                        })
                    })
                })
            });
            //将交互绘图对象添加到地图中
            map.addInteraction(draw);

            //创建测量提示框
            createMeasureTooltip();
            //创建帮助提示框
            createHelpTooltip();

            //定义一个事件监听
            var listener;
            //定义一个控制鼠标点击次数的变量
            var count = 0;
            //绘制开始事件
            draw.on('drawstart', function (evt) {
                //The feature being drawn.
                sketch = evt.feature;
                //提示框的坐标
                var tooltipCoord = evt.coordinate;
                //监听几何要素的change事件
                //Increases the revision counter and dispatches a 'change' event.

                listener = sketch.getGeometry().on('change', function (evt) {
                    //The event target.
                    //获取绘制的几何对象
                    var geom = evt.target;
                    //定义一个输出对象，用于记录面积和长度
                    var output;
                    if (geom instanceof ol.geom.Polygon) {
                        map.removeEventListener('singleclick');
                        map.removeEventListener('dblclick');
                        //输出多边形的面积
                        output = formatArea(geom);
                        //获取多变形内部点的坐标
                        tooltipCoord = geom.getInteriorPoint().getCoordinates();
                    } else if (geom instanceof ol.geom.LineString) {
                        //输出多线段的长度
                        output = formatLength(geom);
                        //获取多线段的最后一个点的坐标
                        tooltipCoord = geom.getLastCoordinate();
                    }

                    //设置测量提示框的内标签为最终输出结果
                    measureTooltipElement.innerHTML = output;
                    //设置测量提示信息的位置坐标
                    measureTooltip.setPosition(tooltipCoord);
                });

                //地图单击事件
                map.on('singleclick', function (evt) {
                    //设置测量提示信息的位置坐标，用来确定鼠标点击后测量提示框的位置
                    measureTooltip.setPosition(evt.coordinate);
                    //如果是第一次点击，则设置测量提示框的文本内容为起点
                    if (count == 0) {
                        measureTooltipElement.innerHTML = "起点";
                    }
                    //根据鼠标点击位置生成一个点
                    var point = new ol.geom.Point(evt.coordinate);
                    //将该点要素添加到矢量数据源中
                    source.addFeature(new ol.Feature(point));
                    //更改测量提示框的样式，使测量提示框可见
                    measureTooltipElement.className = 'tooltip tooltip-static';
                    //创建测量提示框
                    createMeasureTooltip();
                    //点击次数增加
                    count++;
                });

                //地图双击事件
                map.on('dblclick', function (evt) {
                    var point = new ol.geom.Point(evt.coordinate);
                    source.addFeature(new ol.Feature(point));
                });
            }, this);
            //绘制结束事件
            draw.on('drawend', function (evt) {
                count = 0;
                //设置测量提示框的样式
                measureTooltipElement.className = 'tooltip tooltip-static';
                //设置偏移量
                measureTooltip.setOffset([0, -7]);
                //清空绘制要素
                sketch = null;
                //清空测量提示要素
                measureTooltipElement = null;
                //创建测量提示框
                createMeasureTooltip();
                //移除事件监听
                ol.Observable.unByKey(listener);
                //移除地图单击事件
                map.removeEventListener('singleclick');
            }, this);
        }
        //创建帮助提示框
        function createHelpTooltip() {
            //如果已经存在帮助提示框则移除
            if (helpTooltipElement) {
                helpTooltipElement.parentNode.removeChild(helpTooltipElement);
            }
            //创建帮助提示要素的div
            helpTooltipElement = document.createElement('div');
            //设置帮助提示要素的样式
            helpTooltipElement.className = 'tooltip hidden';
            //创建一个帮助提示的覆盖标注
            helpTooltip = new ol.Overlay({
                element: helpTooltipElement,
                offset: [15, 0],
                positioning: 'center-left'
            });
            //将帮助提示的覆盖标注添加到地图中
            map.addOverlay(helpTooltip);
        }
        //创建测量提示框
        function createMeasureTooltip() {
            //创建测量提示框的div
            measureTooltipElement = document.createElement('div');
            measureTooltipElement.setAttribute('id', 'lengthLabel');
            //设置测量提示要素的样式
            measureTooltipElement.className = 'tooltip tooltip-measure';
            //创建一个测量提示的覆盖标注
            measureTooltip = new ol.Overlay({
                element: measureTooltipElement,
                offset: [0, -15],
                positioning: 'bottom-center'
            });
            //将测量提示的覆盖标注添加到地图中
            map.addOverlay(measureTooltip);
        }
        //测量类型发生改变时触发事件
        typeSelect.onchange = function () {
            //移除之前的绘制对象
            map.removeInteraction(draw);
            //重新进行绘制
            addInteraction();
        };

        //格式化测量长度
        var formatLength = function (line) {
            //定义长度变量
            var length;
            //如果大地测量复选框被勾选，则计算球面距离
            if (geodesicCheckbox.checked) {
                //获取坐标串
                var coordinates = line.getCoordinates();
                //初始长度为0
                length = 0;
                //获取源数据的坐标系
                var sourceProj = map.getView().getProjection();
                //进行点的坐标转换
                for (var i = 0; i < coordinates.length - 1; i++) {
                    //第一个点
                    var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
                    //第二个点
                    var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
                    //获取转换后的球面距离
                    //Returns the distance from c1 to c2 using the haversine formula.
                    length += wgs84Sphere.haversineDistance(c1, c2);
                }
            } else {
                //计算平面距离
                length = Math.round(line.getLength() * 100) / 100;
            }
            //定义输出变量
            var output;
            //如果长度大于1000，则使用km单位，否则使用m单位
            if (length > 1000) {
                output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km'; //换算成KM单位
            } else {
                output = (Math.round(length * 100) / 100) + ' ' + 'm'; //m为单位
            }
            return output;
        };

        //格式化测量面积
        var formatArea = function (polygon) {
            //定义面积变量
            var area;
            //如果大地测量复选框被勾选，则计算球面面积
            if (geodesicCheckbox.checked) {
                //获取初始坐标系
                var sourceProj = map.getView().getProjection();
                var geom = polygon.clone().transform(sourceProj, 'EPSG:4326');
                //获取多边形的坐标系
                var coordinates = geom.getLinearRing(0).getCoordinates();
                //获取球面面积
                area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
            } else {
                //获取平面面积
                area = polygon.getArea();
            }
            //定义输出变量
            var output;
            //当面积大于10000时，转换为平方千米，否则为平方米
            if (area > 10000) {
                output = (Math.round(area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>';
            } else {
                output = (Math.round(area * 100) / 100) + ' ' + 'm<sup>2</sup>';
            }
            return output;
        };
        //添加交互绘图对象
        addInteraction();
    });
</script>
</body>

</html>